package com.uniriho.lxr0860_printer;

import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbManager;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;

import com.alibaba.fastjson.JSON;

import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Locale;
import java.util.Vector;

/**
 * 作者：CaoLiulang
 * ❤
 * Date：2022/5/30
 * ❤
 * 模块：USB打印类
 */
public class UsbPrinter {

    private static final String TAG = "UsbPrinter";
    public static final String ACTION_USB_PERMISSION = "com.usb.printer.USB_PERMISSION";
    @SuppressLint("StaticFieldLeak")
    private static UsbPrinter mInstance;
    private Context mContext;
    private PendingIntent mPermissionIntent;
    private UsbManager mUsbManager;
    private UsbDeviceConnection mUsbDeviceConnection;
    private UsbInterface usbInterface;
    private static final int TIME_OUT = 1500;

    private UsbPrinter() {
    }

    public static UsbPrinter getInstance() {
        if (mInstance == null) {
            synchronized (UsbPrinter.class) {
                if (mInstance == null) {
                    mInstance = new UsbPrinter();
                }
            }
        }
        return mInstance;
    }

    /**
     * 初始化打印机，需要与destroy对应
     *
     * @param context 上下文
     */
    public void initPrinter(Context context) {
        init(context);
    }

    @SuppressLint("UnspecifiedImmutableFlag")
    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    private void init(Context context) {
        mContext = context;
        mUsbManager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE);
        mPermissionIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_USB_PERMISSION), 0);
        // 注册广播监听usb设备
        IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
        filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
        filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
        filter.addAction( DeviceConnFactoryManager.ACTION_CONN_STATE );
        mContext.registerReceiver(mUsbDeviceReceiver, filter);
        // 列出所有的USB设备，并且都请求获取USB权限
        HashMap<String, UsbDevice> deviceList = mUsbManager.getDeviceList();
        if (deviceList != null) {
            for (UsbDevice device : deviceList.values()) {
                // 得到此设备的一个接口
                usbInterface = device.getInterface(0);
                Log.i("zsm", "device usbInterface:" + usbInterface);
                // 获取接口的类别 7代表连接的是打印机
                if (usbInterface.getInterfaceClass() == 255) {
                    // 1137     85      1027
                    // 26728     1280      1045+2
                    if (!mUsbManager.hasPermission(device)) {
                        mUsbManager.requestPermission(device, mPermissionIntent);
                    } else {
//                        connectUsbPrinter(device);
                        usbConn(device);
                    }
                    break;
                }
            }
        }
    }

    UsbDevice usbDevice;

    /**
     * usb连接
     *
     * @param device
     */
    private void usbConn(UsbDevice device) {
        this.usbDevice = device;
        Log.i("zsm", "usbDevice：" + usbInterface);
        // 获取设备名称
        String deviceName = device.getDeviceName();

        // 获取VID
        int vendorId = device.getVendorId();

        // 获取PID
        int productId = device.getProductId();

        // 获取版本号
        String version = device.getVersion();

        // 获取设备类别
        int deviceClass = device.getDeviceClass();

        // 获取设备子类别
        int deviceSubclass = device.getDeviceSubclass();

        // 获取设备协议
        int deviceProtocol = device.getDeviceProtocol();

        Log.i("zsm",String.format(Locale.CHINA,"获取deviceId:%d,获取设备名称:%s,获取VID：%d," +
                ",获取PID:%d,获取版本号:%s,获取设备类别:%d,获取设备子类别:%d,获取设备协议:%d",device.getDeviceId(),deviceName,
                vendorId, productId,
                version,deviceClass,deviceSubclass,deviceProtocol));
        new DeviceConnFactoryManager.Build()
                .setId(id)
                .setConnMethod(DeviceConnFactoryManager.CONN_METHOD.USB)
                .setUsbDevice(usbDevice)
                .setContext(mContext)
                .build();
        Log.i("zsm","连接usb:"+usbDevice.getDeviceName()+",id:"+usbDevice.getDeviceClass());
        DeviceConnFactoryManager.getDeviceConnFactoryManagers()[id].openPort();
    }

    int id;
    private final BroadcastReceiver mUsbDeviceReceiver = new BroadcastReceiver() {

        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            Log.i("zsm", "action :" + action);

            if (ACTION_USB_PERMISSION.equals(action)) {
                UsbDevice mUsbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
                Log.i("zsm", "device usbInterface:" + mUsbDevice.getInterface(0));
                synchronized (this) {
                    if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false) && mUsbDevice != null) {
//                        connectUsbPrinter(mUsbDevice);
                        usbConn(mUsbDevice);
                    } else {
                        Log.e("zsm", "USB设备请求被拒绝");
                    }
                }
            } else if (DeviceConnFactoryManager.ACTION_CONN_STATE.equals(action)) {
                int state = intent.getIntExtra(DeviceConnFactoryManager.STATE, -1);
                int deviceId = intent.getIntExtra(DeviceConnFactoryManager.DEVICE_ID, -1);
                    switch (state) {
                        case DeviceConnFactoryManager.CONN_STATE_DISCONNECT:
                            if (id == deviceId) {
                                Log.i("zsm","连接状态2:未连接");
                            }
                            break;
                        case DeviceConnFactoryManager.CONN_STATE_CONNECTING:
                            Log.i("zsm","连接状态:连接中");
                            break;
                        case DeviceConnFactoryManager.CONN_STATE_CONNECTED:
                            Log.i("zsm","连接状态:已连接");
                            break;
                        case DeviceConnFactoryManager.CONN_STATE_FAILED:
                            Log.i("zsm","连接状态:未连接");
                            break;
                        default:
                            break;
                    }
                }
            else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
                UsbDevice mUsbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
                Log.i("zsm", "device usbInterface:" + mUsbDevice.getInterface(0));
                    if (mUsbDevice != null) {
                        Log.e("zsm", "有设备拔出");
                    }
                } else if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) {
                UsbDevice mUsbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
                Log.i("zsm", "device usbInterface:" + mUsbDevice.getInterface(0));
                    Log.e("zsm", "有设备插入");
                    if (mUsbDevice != null) {
                        if (!mUsbManager.hasPermission(mUsbDevice)) {
                            mUsbManager.requestPermission(mUsbDevice, mPermissionIntent);
                        }
                    }
                }
            }
        }

        ;

        /**
         * 关闭usb的端口及取消注册的广播监听
         */
        public void close() {
            if (mUsbDeviceConnection != null) {
                mUsbDeviceConnection.close();
                mUsbDeviceConnection = null;
            }
            mContext.unregisterReceiver(mUsbDeviceReceiver);
            mContext = null;
            mUsbManager = null;
        }

        // 过滤此USB的端点。用来发送和接收数据
        UsbEndpoint usbEndpointOut = null;
        UsbEndpoint usbEndpointIn = null;

        /**
         * 连接打印机设备
         *
         * @param mUsbDevice 识别到的打印机设备
         */
        private void connectUsbPrinter(UsbDevice mUsbDevice) {
            if (mUsbDevice != null) {
                mUsbDeviceConnection = mUsbManager.openDevice(mUsbDevice);
                Log.i("zsm", "openDevice " + mUsbDeviceConnection);

                for (int i = 0; i < usbInterface.getEndpointCount(); i++) {
                    UsbEndpoint usbEndpoint = usbInterface.getEndpoint(i);
                    int direction = usbEndpoint.getDirection();
                    int type = usbEndpoint.getType();
                    if (type != UsbConstants.USB_ENDPOINT_XFER_BULK) {
                        //过滤不支持的USB协议类型
                        continue;
                    }
                    if (direction == UsbConstants.USB_DIR_OUT) {
                        usbEndpointOut = usbEndpoint;
                    } else if (direction == UsbConstants.USB_DIR_IN) {
                        usbEndpointIn = usbEndpoint;
                    }

                    if (usbEndpointIn != null && usbEndpointOut != null) {
                        break;
                    }
                }
                if (usbEndpointIn != null && usbEndpointOut != null) {
                    mUsbDeviceConnection = mUsbManager.openDevice(mUsbDevice);
                    if (mUsbDeviceConnection != null) {
                        Log.e("zsm", "设备已连接");
                        // 在使用UsbInterface进行数据的写入写出之前，要申明对其的专有访问权限，防止通信混乱
                        boolean claimInterface = mUsbDeviceConnection.claimInterface(usbInterface, true);
                        Log.e("zsm", "claimInterface " + claimInterface);


                    }
                } else {
                    Log.e("zsm", "未匹配打印机");
                }

            } else {
                Log.e("zsm", "未发现可用的打印机");
            }
        }

        /**
         * 安卓9.0之前
         * 只要你传送的数据不大于16384 bytes，传送不会出问题，一旦数据大于16384 bytes，也可以传送，
         * 只是大于16384后面的数据就会丢失，获取到的数据永远都是前面的16384 bytes，
         * 所以，android USB Host 模式与HID使用bulkTransfer（endpoint，buffer，length，timeout）通讯时
         * buffer的长度不能超过16384。
         * <p>
         * controlTransfer( int requestType, int request , int value , int index , byte[] buffer , int length , int timeout)
         * 该方法通过0节点向此设备传输数据，传输的方向取决于请求的类别，如果requestType 为 USB_DIR_OUT 则为写数据 ， USB _DIR_IN ,则为读数据
         */
        public void write(byte[] bytes) {
            if (mUsbDeviceConnection != null) {
                // 通过给定的endpoint 来进行大量的数据传输，传输的方向取决于该节点的方向，
                // buffer是要发送或接收的字节数组，length是该字节数组的长度，失败则返回负数
                // 下行端点，字节数组消息，消息长度，响应时间
                int receivedBytes = mUsbDeviceConnection.bulkTransfer(usbEndpointOut, bytes, bytes.length, TIME_OUT);
                Log.i("zsm", "write len：" + receivedBytes);
                if (receivedBytes > 0) {
                    byte[] buffer = new byte[receivedBytes];
                    int ret = mUsbDeviceConnection.bulkTransfer(usbEndpointIn, buffer, buffer.length, TIME_OUT);
                    // 进行数据处理...
                    Log.i("zsm", "receive:" + Arrays.toString(buffer));
                }
            } else {
                Log.i("zsm", "write fail：mUsbDeviceConnection");
                if (Looper.myLooper() == null) {
                    Looper.prepare();
                }
                handler.sendEmptyMessage(0);
                Looper.loop();
            }
        }

        @SuppressLint("HandlerLeak")
        private final Handler handler = new Handler(Looper.getMainLooper()) {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                Log.e("zsm", "未发现可用的打印机");
            }
        };

        /**
         * 初始化
         */
        public void init() {
            write(ESCUtil.initPrinter());
        }

        /**
         * 打印文字
         *
         * @param msg 打印的内容
         */
        public void printText(String msg) {
            byte[] bytes = new byte[0];
            try {
                bytes = msg.getBytes("gbk");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            write(bytes);
        }

        /**
         * 换行打印文字
         *
         * @param msg 打印的内容
         */
        public void printTextNewLine(String msg) {
            byte[] bytes = new byte[0];
            try {
                bytes = msg.getBytes("gbk");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            write(new String("\n").getBytes());
            write(bytes);
        }

        public void printData(Vector<Byte> datas) {
            byte[] byties = new byte[datas.size()];
            int i = 0;
            for (Byte data : datas) {
                byties[i++] = data;
            }
            DeviceConnFactoryManager.getDeviceConnFactoryManagers()[0].sendDataImmediately(datas);
//        write(byties);

        }

        /**
         * 换行
         */
        public void printLine() {
//        write(new byte[]{10});
            write(new String("\n").getBytes());
        }

        /**
         * 打印空行
         *
         * @param size 几行
         */
        public void printLine(int size) {
            for (int i = 0; i < size; i++) {
                printText("\n");
            }
        }

        /**
         * 设置字体大小
         *
         * @param size 0:正常大小 1:两倍高 2:两倍宽 3:两倍大小 4:三倍高 5:三倍宽 6:三倍大 7:四倍高 8:四倍宽 9:四倍大小 10:五倍高 11:五倍宽 12:五倍大小
         */
        public void setTextSize(int size) {
            write(ESCUtil.setTextSize(size));
        }

        /**
         * 字体加粗
         *
         * @param isBold true/false
         */
        public void bold(boolean isBold) {
            if (isBold) {
                write(ESCUtil.boldOn());
            } else {
                write(ESCUtil.boldOff());
            }
        }

        /**
         * 打印一维条形码
         *
         * @param data 条码
         */
        public void printBarCode(String data) {
//        write(ESCUtil.getPrintBarCode(data, 5, 90, 5, 2));
            write(ESCUtil.getPrintBarCode(data, 5, 90, 4, 2));
        }

        /**
         * 打印二维码
         *
         * @param data 打印的内容
         */
//    public void printQrCode(String data) {
//        write(ESCUtil.getPrintQrCode(data, 250));
//    }

        /**
         * 设置对齐方式
         *
         * @param position 0居左 1居中 2居右
         */
        public void setAlign(int position) {
            byte[] bytes = null;
            switch (position) {
                case 0:
                    bytes = ESCUtil.alignLeft();
//                bytes = new byte[]{ 0x1b, 0x61, 0x30 };
//                bytes = new byte[]{27, 97, (byte) 0};
                    break;
                case 1:
                    bytes = ESCUtil.alignCenter();
//                bytes = new byte[]{ 0x1b, 0x61, 0x31 };
//                bytes = new byte[]{27, 97, (byte) 1};
                    break;
                case 2:
                    bytes = ESCUtil.alignRight();
//                bytes = new byte[]{ 0x1b, 0x61, 0x32 };
//                bytes = new byte[]{27, 97, (byte) 2};
                    break;
                default:
                    break;
            }
            write(bytes);
        }

        /**
         * 获取字符串的宽度
         *
         * @param str 取字符
         * @return 宽度
         */
        public int getStringWidth(String str) {
            int width = 0;
            for (char c : str.toCharArray()) {
                width += isChinese(c) ? 2 : 1;
            }
            return width;
        }

        /**
         * 判断是否中文
         * GENERAL_PUNCTUATION 判断中文的“号
         * CJK_SYMBOLS_AND_PUNCTUATION 判断中文的。号
         * HALFWIDTH_AND_FULLWIDTH_FORMS 判断中文的，号
         *
         * @param c 字符
         * @return 是否中文
         */
        public static boolean isChinese(char c) {
            Character.UnicodeBlock ub = Character.UnicodeBlock.of(c);
            return ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS
                    || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS
                    || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A
                    || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B
                    || ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION
                    || ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS
                    || ub == Character.UnicodeBlock.GENERAL_PUNCTUATION;
        }

        /**
         * 切纸
         */
        public void cutPager() {
            write(ESCUtil.cutter());
        }

    }
